<?php
/**
 * 数据库索引类
 *
 * 使用的Memcached：
 *      indexer：集群(包含数据库配置和用户数据库索引)
 *
 * 数据库：
 *  数据索引库 : indexer
 * 		索引数据表 : index_000 ~ index099
 * 		数据库配置 : dbconfig
 */

class Indexer
{
	/**
	 * 用户数据库
	 */
	const DB_TYPE_USER = 1;
    
    /**
     * 非空闲
     * @var int
     */
    const DB_STATUS_FULL = 0;
    
    /**
     * 空闲
     * @var int 
     */
    const DB_STATUS_EMPTY = 1;

	/**
	 * 用户ID
	 * @var	int
	 */
	protected $uId;

	/**
	 * 用户的DB ID
	 *
	 * @var	int
	 */
	protected $dbId;

	/**
	 * 索引缓存KEY命名空间
	 */
	const INDEXER_CACHEKEY_NAMESPACE = '_dbIndexer';

	/**
	 * 不存在的用户标记KEY命名空间
	 */
	const NOTEXIST_CACHEKEY_NAMESPACE = '_dbIndexer_flag';

	/**
	 * 默认分表大小
	 * @var int
	 */
	const TABLE_COUNT = 100;
    
    
    /**
	 * 单例对象
	 */
	public static $singletonObjects = array();

    /**
	 * 获取单例
	 * @param $uId
	 * @return Indexer
	 */
	public static function getInstance( $uId )
	{
		if( !isset( self::$singletonObjects[$uId] ) )
		{
			self::$singletonObjects[$uId] = new self( $uId );
		}

		return self::$singletonObjects[$uId];
	}

	protected function __construct( $uId )
	{
		if( $uId < 1 )
		{
                   
			throw new AppException( 5 );
        }
		
		$this->uId = $uId;

		//获取用户所在数据库Id
		$this->dbId = self::getUserIndex( $uId , true );
	}

	/**
	 * 获取数据库实例
	 * @param	int $dbType
	 * @return	Helper_MysqlDb
	 */
	public function getDb( $dbType = self::DB_TYPE_USER )
	{
             
		//实例化用户数据库
		return self::getDbInstance( $this->dbId , $dbType );
	}

	/**
	 * 根据DB类型获取DB名字
	 * @param	int $dbType	DB类型
	 * @return	string
	 */
	public function getTypeToDbName( $dbType )
	{
		switch( $dbType )
		{
			case self::DB_TYPE_USER:
				return "user";
				break;
		}
	}

	/**
	 * 获取用户所在表Id(按照用户Id与分表数量取模)
	 * @param bigint $uId
	 * @param int $div
	 * @return	int
	 */
	public static function getTableId( $uId , $div = self::TABLE_COUNT )
	{
		return str_pad( $uId % $div , 3 , "0" , STR_PAD_LEFT );
	}

    /**
    * 计算哈希ID
    * @param    int $param
    * @param    int $count
    * @return   int
    */
    public static function getDbIndex( $param , $count )
    {
       return sprintf( "%u" , crc32( $param ) ) % $count;
    }

	/**
	 * 从索引数据库获取用户所在数据库ID
	 * 不存在索引则创建索引
	 *
	 * @param bigint $uId
	 * @param boolean $autoAdd //是否自动增加索引
	 * @return Int
	 */
	public static function getUserIndex( $uId , $autoAdd = false )
	{
		//从缓存获取用户数据库索引
        $cacheInstance = self::_getCache();
		$indexCachekey = $uId . self::INDEXER_CACHEKEY_NAMESPACE;
		$dbId = $cacheInstance->get( $indexCachekey );		//获取用户数据库索引
		if( $dbId > 0 )	//用户数据库必须ID大于0
		{
			return intval( $dbId );
		}
		
		$notExistKey = $uId . self::NOTEXIST_CACHEKEY_NAMESPACE;
		if( $autoAdd == false )
		{
			//从不存在缓存中获取
			if( $cacheInstance->get( $notExistKey ) )
			{
				return false;
			}
		}

		//从数据库获取用户db索引
        $dbInstance = self::_getDb();
		$tId =  self::getTableId( $uId );
        $info = $dbInstance->get( "index_{$tId}" , array( 'uId' => $uId ) );

		//存在索引则设置缓存并返回
		if( isset( $info['dbId'] ) && $info['dbId'] > 0 )
		{
			$dbId = intval( $info['dbId'] );
			
			//设置db索引缓存
			$cacheInstance->set( $indexCachekey , $dbId );
			return intval( $dbId );
		}

		if( $autoAdd == false )
		{
			//增加不存在用户缓存
			$cacheInstance->add( $notExistKey , 1 , 3600 );
			return false;
		}

		$dbId = self::_addUserIndexToDb( $uId );
		if( $dbId > 0 )
		{
			$cacheInstance->set( $indexCachekey , $dbId );

			//删除不存在标志
			$cacheInstance->delete( $notExistKey );
		}

		return $dbId;
	}

	/**
	 * 根据算法从当前空闲数据库中确定一台数据库存放用户信息,并将用户与数据库索引写入数据库
	 * @return	int
	 */
	private function _addUserIndexToDb( $uId )
	{
		//从当前空闲的db中hash一台db
		$activityDbConfig = self::getDbStatus( true );
		$count = count( $activityDbConfig );
		if( $count < 1 )
		{
			return false;
		}

		$dbIndex =  self::getDbIndex( $uId , $count );	//按照算法确定一台数据库
		$dbId =  empty( $activityDbConfig[$dbIndex]['dbId'] ) ? 0 : intval( $activityDbConfig[$dbIndex]['dbId'] );
		if( $dbId < 1 )
		{
			return false;
		}

		//建立db索引数据库
		$tId = self::getTableId( $uId );
		$dbInstance = self::_getDb();
        $dbInstance->add( "index_{$tId}" , array( 'uId' => $uId , 'dbId' => $dbId ) );

		//更新当前数据库总用户数
        $dbInstance->update( "db_status" , array( 'dbId' => $dbId ) , "`totalUser` = `totalUser` + 1" , 1 );
		return $dbId;
	}

	/**
	 * 获取数据库实例
	 * @param int $dbType
	 * @param int $dbId //数据库ID
	 * @return object //数据库实例
	 */
	public static function getDbInstance( $dbId , $dbType = self::DB_TYPE_USER )
	{
		static $db = array();	//静态缓存db实例
		if( empty( $db[$dbType][$dbId] ) )
		{
			//获取db配置
			$config = self::getDbConfig( $dbType , $dbId );
			
			//实例化并缓存db
			$db[$dbType][$dbId] = new Helper_MysqlDb( $config );
		}

		return $db[$dbType][$dbId];
	}

	/**
	 * 获取数据库配置
	 * @author Tom
	 * @param String $dbName	//数据库名
	 * @param Int $dbId	//数据库id
	 * @return Array
	 */
	protected function getDbConfig( $dbType , $dbId = null )
	{
		$dbName = self::getTypeToDbName( $dbType );
		$cacheInstance = self::_getCache();	//获取索引缓存实例
		$dbConfig =  $cacheInstance->get( "dbConfig_{$dbName}" );	//从缓存获取db配置
		if( !$dbConfig )
		{
            //从db配置数据表获取db配置
			$dbInstance = self::_getDb();
			$info = $dbInstance->getList( "dbconfig_{$dbName}" , array( 1 => 1 ) );
			$dbConfig = array();
			foreach ( $info as $item )
			{
				$dbConfig[$item['dbId']] = $item;
			}

			//将db配置缓存
			$cacheInstance->set( "dbConfig_{$dbName}" , $dbConfig );
		}

		if( is_null( $dbId ) )
			return $dbConfig;	//返回所有数据库配置

		return $dbConfig[$dbId];	// 返回某个数据库配置
	}

	/**
	 * 获取数据库状态
	 *
	 * @param int $status	//状态(0:非空闲,1:空闲)
	 */
	public static function getDbStatus( $activity = false )
	{
        $cacheInstance = self::_getCache();
		$dbStatus =  $cacheInstance->get( 'dbStatus' );	//从缓存获取数据库状态
		if( !$dbStatus )
		{
			//从db配置数据表获取db配置
			$dbInstance = self::_getDb();
            $info = $dbInstance->getList( "db_status" , array( 1 => 1 ) );
			$dbStatus = array();
			foreach ( $info as $item )
			{
				$dbStatus['all'][$item['dbId']] = $item;	//所有的db配置
				if( $item['status'] == self::DB_STATUS_EMPTY )
				{
                    //设置空闲的db
					$dbStatus['activity'][] = $item;	//空闲的db配置
				}
			}
            
			//将db配置缓存
			$cacheInstance->set( 'dbStatus' , $dbStatus );
		}

		if( $activity )
			return $dbStatus['activity'];

		return $dbStatus['all'];
	}
    
    /**
     * 获取索引缓存实例
     * @return Core_Cache_Memcached 
     */
    protected static function _getCache()
    {
        return Common::getCache( 'indexer' );
    }
    
    /**
     * 获取索引数据库实例
     * @return Helper_MysqlDb
     */
    protected static function _getDb()
    {
        return Common::getMysql( 'indexer' );
    }
}